home *** CD-ROM | disk | FTP | other *** search
/ MacWorld 1996 April / Macworld (1996-04).dmg / Shareware World / Entertainment / Arcade / CheeseToast / Source / Sprites.c < prev    next >
Text File  |  1994-03-10  |  22KB  |  940 lines

  1. /****************************************************************************
  2.  * Sprites.c
  3.  *
  4.  *        Sprite Maintenence, Logic, Animation
  5.  *
  6.  *        Most of Game Logic is here...
  7.  *
  8.  ****************************************************************************/
  9. #include "CToast.h"
  10. #include <stdlib.h>
  11. #include <math.h>
  12.  
  13. #if __option(profile)                    // 6/15 Optional profiling support
  14. #include <Profile.h>
  15. #endif
  16.  
  17.  
  18. // Sprites Initialization Functions
  19.  
  20. void NewSaveMap(SpriteInstance *sp)
  21. {
  22.     short    n;
  23.     if (gNbrSaveMaps == MaxSaveMaps - 1) {
  24.         DebugStr("\pMax Save Maps");
  25.         return;
  26.     }
  27.     n = gNbrSaveMaps;
  28.     gNbrSaveMaps++;
  29.     smTable[n].active = true;
  30.     smTable[n].sp = sp;
  31.     // sp->saveMapIdx = n;
  32.     sp->saveMapPtr = &smTable[n];
  33. }
  34.  
  35. void KillSaveMap(SaveMapPtr sm)
  36. {
  37.     sm->active = false;
  38.     sm->sp = NULL;
  39.     while (gNbrSaveMaps > 0 && !smTable[gNbrSaveMaps-1].active)
  40.         --gNbrSaveMaps;
  41. }
  42.  
  43. SpriteInstance *NewSprite(Boolean saveMapFlag)
  44. {
  45.     short            i;
  46.     SpriteInstance *sp;
  47.     if (gMaxSprite == MaxSprites - 1) {
  48.         DebugStr("\pMax Sprites");
  49.         return NULL;
  50.     }
  51.     sp = &sTable[gMaxSprite++];
  52.     sp->active = true;
  53.     if (saveMapFlag)
  54.         NewSaveMap(sp);
  55.     else
  56.         sp->saveMapPtr = NULL;
  57.     ++gSpriteCnt;
  58.     return sp;
  59. }
  60.  
  61. void KillSprite(SpriteInstance *sp)
  62. {
  63.     sp->active = 0;
  64.     if (sp->saveMapPtr) {
  65.         KillSaveMap(sp->saveMapPtr);
  66.         // sp->saveMapPtr = NULL;    // Don't kill - we still have to update sprite
  67.     }
  68.     --gSpriteCnt;
  69.     while (gMaxSprite > 0 && !sTable[gMaxSprite-1].active && !sTable[gMaxSprite-1].update)
  70.         --gMaxSprite;
  71. }
  72.  
  73. void NewAsteroid(short type)
  74. {
  75.     register short    vecSpeed;
  76.     register SpriteInstance *sp;
  77.     if ((sp = NewSprite(true)) == NULL)
  78.         return;
  79.     if (MyRandom(50) == 0)
  80.         type = ST_Jim;
  81.     sp->type = type;
  82.     sp->param1 = 0;
  83.     sp->aniState = MyRandom(sDef[sp->type].nbrIcons);
  84.     if (MyRandom(2) == 0) {
  85.         sp->pos.h = 0;
  86.         sp->pos.v = MyRandom(gPlayRect.bottom);
  87.     }
  88.     else {
  89.         sp->pos.h = MyRandom(gPlayRect.right);
  90.         sp->pos.v = 0;
  91.     }
  92.     sp->oldPos = sp->pos;
  93.     // Pick any angle except 0,4,8,12 (which are straight)
  94.     // and may cause asteroid to be invisible on sides
  95.     sp->angle = MyRandom(MaxAngles-4);
  96.     sp->angle += 1+(sp->angle/7);
  97.     vecSpeed = MyRandom(3)+2+MyRandom(gGameLevel);
  98.     sp->vector.lh = vecTable[sp->angle].lh * vecSpeed;
  99.     sp->vector.lv = vecTable[sp->angle].lv * vecSpeed;
  100.     if (labs(sp->vector.lh) < 0x00010000)
  101.         sp->vector.lh = 0x00010000;
  102.     if (labs(sp->vector.lv) < 0x00010000)
  103.         sp->vector.lv = 0x00010000;
  104.     sp->aniSpeed = 1 + MyRandom(2);
  105.     sp->tickCtr = MyRandom(sp->aniSpeed);
  106.     sp->width = 32;
  107.     ++gAsteroidCnt;
  108. }
  109.  
  110. #if DEBUGGING
  111. // This puts two special sprites in the lower left hand corner of the screen
  112. // which are used to monitor sprite memory
  113. //
  114. void NewDebugDisplay(void)
  115. {
  116.     register SpriteInstance *sp;
  117.     if ((sp = NewSprite(false)) == NULL)
  118.         return;
  119.     sp->type = ST_SpriteCnt;
  120.     sp->param1 = 0;
  121.     sp->pos.h = 40;
  122.     sp->pos.v = 480+32;
  123.     sp->oldPos = sp->pos;
  124.     sp->width = 2;
  125.  
  126.     if ((sp = NewSprite(false)) == NULL)
  127.         return;
  128.     sp->type = ST_MaxSprite;
  129.     sp->param1 = 0;
  130.     sp->pos.h = 32;
  131.     sp->pos.v = 480+32;
  132.     sp->oldPos = sp->pos;
  133.     sp->width = 2;
  134. }
  135. #endif
  136.  
  137. void NewSubAsteroid(short parent, short type, short vecOffset)
  138. {
  139.     register short vecSpeed;
  140.     register SpriteInstance *sp,*par;
  141.     if ((sp = NewSprite(true)) == NULL)
  142.         return;
  143.     par = &sTable[parent];
  144.     sp->pos = par->pos;
  145.     sp->oldPos = par->oldPos;
  146.     sp->param1 = par->param1;
  147.     sp->param2 = par->param2;
  148.     sp->aniState = par->aniState;
  149.     sp->lifeSpan = par->lifeSpan;
  150.     sp->angle = par->angle;
  151.     sp->vector = par->vector;
  152.  
  153.     sp->type = type;
  154.     sp->angle += vecOffset;
  155.     if (sp->angle < 0)
  156.         sp->angle += MaxAngles;
  157.     if (sp->angle >= MaxAngles)
  158.         sp->angle -= MaxAngles;
  159.     vecSpeed = MyRandom(3)+2+MyRandom(gGameLevel);
  160.     sp->vector.lh = vecTable[sp->angle].lh * vecSpeed;
  161.     sp->vector.lv = vecTable[sp->angle].lv * vecSpeed;
  162.     sp->aniSpeed = 1 + MyRandom(2);
  163.     sp->tickCtr = 0;
  164.     sp->width = 32;
  165.     ++gAsteroidCnt;
  166. }
  167.  
  168. void NewSaucer(void)
  169. {
  170.     register SpriteInstance *sp;
  171.     if ((sp = NewSprite(true)) == NULL)
  172.         return;
  173.     sp->type = ST_Saucer;
  174.     sp->param1 = 2+gGameLevel/2;            // Max Hits
  175.     sp->param2 = 0;                            // Hits Taken
  176.     sp->aniState = MyRandom(sDef[sp->type].nbrIcons);
  177.     if (MyRandom(2) == 0) {
  178.         sp->pos.h = 0;
  179.         sp->pos.v = MyRandom(gPlayRect.bottom);
  180.     }
  181.     else {
  182.         sp->pos.h = MyRandom(gPlayRect.right);
  183.         sp->pos.v = 0;
  184.     }
  185.     sp->oldPos = sp->pos;
  186.     sp->angle = MyRandom(MaxAngles-4);
  187.     sp->angle += 1+sp->angle/7;
  188.     sp->vector.lh = vecTable[sp->angle].lh;
  189.     sp->vector.lv = vecTable[sp->angle].lv;
  190.     sp->aniSpeed = 2;
  191.     sp->tickCtr = 0;
  192.     sp->width = 32;
  193.     PlaySound(S_BadGuyDebut, 3);
  194. }
  195.  
  196. void NewBarbell(void)
  197. {
  198.     register SpriteInstance *sp;
  199.     if ((sp = NewSprite(true)) == NULL)
  200.         return;
  201.     sp->type = ST_Barbell;
  202.     sp->param1 = 3+gGameLevel/2;            // Max Hits
  203.     sp->param2 = 0;                            // Hits Taken
  204.     sp->aniState = MyRandom(sDef[sp->type].nbrIcons);
  205.     if (MyRandom(2) == 0) {
  206.         sp->pos.h = 0;
  207.         sp->pos.v = MyRandom(gPlayRect.bottom);
  208.     }
  209.     else {
  210.         sp->pos.h = MyRandom(gPlayRect.right);
  211.         sp->pos.v = 0;
  212.     }
  213.     sp->oldPos = sp->pos;
  214.     sp->angle = MyRandom(MaxAngles-4);
  215.     sp->angle += 1+sp->angle/7;
  216.     sp->vector.lh = vecTable[sp->angle].lh;
  217.     sp->vector.lv = vecTable[sp->angle].lv;
  218.     sp->aniSpeed = 2;
  219.     sp->tickCtr = 0;
  220.     sp->width = 32;
  221.     sp->lifeSpan = 200;
  222.     PlaySound(S_BadGuyDebut, 3);
  223. }
  224.  
  225. void NewCube(void)
  226. {
  227.     register SpriteInstance *sp;
  228.     if ((sp = NewSprite(true)) == NULL)
  229.         return;
  230.     sp->type = ST_Cube;
  231.     sp->param1 = 4+gGameLevel/2;            // Max Hits
  232.     sp->param2 = 0;                            // Hits Taken
  233.     sp->aniState = MyRandom(sDef[sp->type].nbrIcons);
  234.     if (MyRandom(2) == 0) {
  235.         sp->pos.h = 0;
  236.         sp->pos.v = MyRandom(gPlayRect.bottom);
  237.     }
  238.     else {
  239.         sp->pos.h = MyRandom(gPlayRect.right);
  240.         sp->pos.v = 0;
  241.     }
  242.     sp->oldPos = sp->pos;
  243.     sp->angle = MyRandom(MaxAngles-4);
  244.     sp->angle += 1+sp->angle/7;
  245.     sp->vector.lh = vecTable[sp->angle].lh;
  246.     sp->vector.lv = vecTable[sp->angle].lv;
  247.     sp->aniSpeed = 2;
  248.     sp->tickCtr = 0;
  249.     sp->width = 32;
  250.     sp->lifeSpan = 600;
  251.     PlaySound(S_BadGuyDebut, 3);
  252. }
  253.  
  254.  
  255. void NewShipTimer(register SpriteInstance *sp)
  256. {
  257.     if (sp->tickCtr++ > 60) {
  258.         sp->tickCtr = 0;
  259.         sp->type = ST_Teapot;
  260.         PlaySound(S_ShipDebut, 3);
  261.         gShipMode |= SM_AutoShield;
  262.         gShieldPower = MaxShieldPower;
  263.     }
  264. }
  265.  
  266. void NewShip(void)
  267. {
  268.     register SpriteInstance *sp;
  269.     if (gRemainingShips == 0) {
  270.         gGameState = GS_GameOver;
  271.         return;
  272.     }
  273.     if ((sp = NewSprite(true)) == NULL)
  274.         return;
  275.     gShip = sp;
  276.     sp->type = ST_TeapotNew;
  277.     sp->param1 = 0;
  278.     sp->pos = gCenterP;
  279.     sp->oldPos = sp->pos;
  280.     sp->angle = 0;
  281.     sp->aniState = 0;
  282.     sp->vector.lh = 0L;
  283.     sp->vector.lv = 0L;
  284.     sp->aniSpeed = 0;
  285.     sp->tickCtr = 0;
  286.     sp->width = 32;
  287.     --gRemainingShips;
  288.     gShipMode = 0;
  289. }
  290.  
  291. void NewYummy(void)
  292. {
  293.     register SpriteInstance *sp;
  294.     if ((sp = NewSprite(true)) == NULL)
  295.         return;
  296.     sp->type = ST_Yummies;
  297.     sp->aniState = MyRandom(NbrYummies);
  298.     sp->param1 = sp->aniState;
  299.     sp->pos.h = IconWidth+MyRandom(gPlayRect.right-IconWidth);
  300.     sp->pos.v = IconHeight+MyRandom(gPlayRect.bottom-IconWidth);
  301.     sp->vector.lh = 0;
  302.     sp->vector.lv = 0;
  303.     sp->oldPos = sp->pos;
  304.     sp->lifeSpan = 100;
  305.     sp->tickCtr = 0;
  306.     sp->width = 32;
  307.     StandardSpriteDraw(sp);
  308.     ++gYummyCnt;
  309. }
  310.  
  311. void NewQuake(void)
  312. {
  313.     sDef[ST_StatusDisplay].moveFunc = QuakeMove;
  314.     sTable[0].param2 = 40;
  315. }
  316.  
  317. void LaunchBullet(short type, short x, short y, long vx, long vy, short lifeSpan, long bTarget)
  318. {
  319.     register SpriteInstance *sp;
  320.     if ((sp = NewSprite(false)) == NULL)
  321.         return;
  322.     sp->type = type;
  323.     sp->param1 = bTarget;
  324.     sp->pos.h = x;
  325.     sp->pos.v = y;
  326.     sp->oldPos = sp->pos;
  327.     sp->vector.lh = vx;
  328.     sp->vector.lv = vy;
  329.     sp->tickCtr = 0;
  330.     sp->lifeSpan = lifeSpan;
  331.     sp->width = 2;
  332. }
  333.  
  334. void LaunchSpark(short x, short y, long vx, long vy, short lifeSpan, short color)
  335. {
  336.     register SpriteInstance *sp;
  337.     if ((sp = NewSprite(false)) == NULL)
  338.         return;
  339.     sp->type = ST_Spark;
  340.     sp->param1 = color;
  341.     sp->pos.h = x;
  342.     sp->pos.v = y;
  343.     sp->oldPos = sp->pos;
  344.     sp->vector.lh = vx;
  345.     sp->vector.lv = vy;
  346.     sp->tickCtr = 0;
  347.     sp->lifeSpan = lifeSpan;
  348.     sp->width = 2;
  349.     ++gSparkCnt;
  350. }
  351.  
  352. // Specialized Sprites - Kill Functions
  353.  
  354. void ConsumeYummy(register SpriteInstance *sp)
  355. {
  356.     switch (sp->param1) {
  357.     case 0:        gScoreMultiply *= 2;        break;
  358.     case 1:        gScoreMultiply *= 3;        break;
  359.     case 2:        gScoreMultiply *= 5;        break;
  360.     case 3:        gShipMode |= SM_Triple;        break;
  361.     case 4:        gShipMode |= SM_Uzi;        break;
  362.     }
  363.     StandardSpriteErase(sp);
  364.     KillSprite(sp);
  365.     PlaySound(S_YummyConsume, 3);
  366.     --gYummyCnt;
  367. }
  368.  
  369. void ExplodeSprite(register SpriteInstance *sp, register short lifeSpan)
  370. {
  371.     register long    i;
  372.     register short    r;
  373.     register Ptr    colorPtr, maskPtr;
  374.     colorPtr = sDef[sp->type].colorMaps + AniFrameIndex(sp->aniState);
  375.     maskPtr = sDef[sp->type].maskMaps + AniFrameIndex(sp->aniState);
  376.     r = 0;
  377.     for (i = 0; i < 1024; i += 12) {
  378.         if (!(*maskPtr)) {
  379.             LaunchSpark(sp->pos.h+(i&31),sp->pos.v+(i>>5),
  380.                          sp->vector.lh + (vecTable[r].lh << 1),
  381.                          sp->vector.lv + (vecTable[r].lv << 1),
  382.                          lifeSpan,
  383.                          *colorPtr);
  384.             ++r;
  385.             r &= 0x0F;
  386.         }
  387.         colorPtr += 12;
  388.         maskPtr += 12;
  389.     }
  390.     KillSprite(sp);
  391.     if (lifeSpan > 10)
  392.         PlaySound(S_ShipExplodes, 3);
  393.     else
  394.         PlaySound(S_Explosion, 2);
  395. }
  396.  
  397.  
  398. // Sprite Move Functions
  399.  
  400. void StandardSpriteMove(register SpriteInstance *sp)
  401. {
  402.     // Update position
  403.     sp->pos.h += (sp->vector.lh >> 16);
  404.     sp->pos.v += (sp->vector.lv >> 16);
  405.     if (sp->pos.h < gPlayRect.left)
  406.         sp->pos.h += gPlayWidth;
  407.     if (sp->pos.v < gPlayRect.top)
  408.         sp->pos.v += gPlayHeight;
  409.     if (sp->pos.h > gPlayRect.right)
  410.         sp->pos.h -= gPlayWidth;
  411.     if (sp->pos.v > gPlayRect.bottom)
  412.         sp->pos.v -= gPlayHeight;
  413.  
  414.     // Update Animation Frame
  415.     sp->tickCtr++;
  416.     if (sp->tickCtr >= sp->aniSpeed) {
  417.         sp->tickCtr = 0;
  418.         sp->aniState++;
  419.         if (sp->aniState >= sDef[sp->type].nbrIcons)
  420.             sp->aniState = 0;
  421.     }
  422. }
  423.  
  424. #if DEBUGGING
  425.  
  426. void MaxSpriteMove(register SpriteInstance *sp)
  427. {
  428.     sp->pos.h = 32;
  429.     sp->pos.v = 480+32-gMaxSprite;
  430. }
  431.  
  432. void SpriteCntMove(register SpriteInstance *sp)
  433. {
  434.     sp->pos.h = 40;
  435.     sp->pos.v = 480+32-gSpriteCnt;
  436. }
  437.  
  438. #endif
  439.  
  440. void SaucerMove(register SpriteInstance *sp)
  441. {
  442.     // Update position
  443.     sp->pos.h += (sp->vector.lh >> 16);
  444.     sp->pos.v += (sp->vector.lv >> 16);
  445.     if (sp->pos.h < gPlayRect.left)
  446.         sp->pos.h += gPlayWidth;
  447.     if (sp->pos.v < gPlayRect.top)
  448.         sp->pos.v += gPlayHeight;
  449.     if (sp->pos.h > gPlayRect.right)
  450.         sp->pos.h -= gPlayWidth;
  451.     if (sp->pos.v > gPlayRect.bottom)
  452.         sp->pos.v -= gPlayHeight;
  453.  
  454.     // Update Animation Frame
  455.     sp->tickCtr++;
  456.     if (sp->tickCtr >= sp->aniSpeed) {
  457.         sp->tickCtr = 0;
  458.         sp->aniState++;
  459.         if (sp->aniState >= sDef[sp->type].nbrIcons) {
  460.             LongPoint    newVec;
  461.             sp->aniState = 0;
  462.             if (gShip && gShip->type == ST_Teapot || gShip->type == ST_TeapotT) {
  463.                 double scale;
  464.                 register short    slowness;
  465.                 newVec.lh = gShip->pos.h - sp->pos.h;
  466.                 newVec.lv = gShip->pos.v+12 - sp->pos.v;            
  467.                 scale = 1.0 / sqrt(    (double) newVec.lh*newVec.lh +
  468.                         newVec.lv*newVec.lv);
  469.                 newVec.lh *= scale * 65536.0;
  470.                 newVec.lv *= scale * 65536.0;
  471.                 newVec.lh <<= 3;
  472.                 newVec.lv <<= 3;
  473.                 slowness = 1 + MyRandom(2);
  474.                 sp->vector.lh = newVec.lh >> slowness;
  475.                 sp->vector.lv = newVec.lv >> slowness;
  476.                 LaunchBullet(ST_Photon,
  477.                              sp->pos.h+16,sp->pos.v+4,
  478.                              newVec.lh,newVec.lv,
  479.                              25,
  480.                              BF_BadBullet);
  481.  
  482.                 PlaySound(S_EnemyFires, 2);
  483.             }
  484.         }
  485.     }
  486. }
  487.  
  488. void BarbellMove(register SpriteInstance *sp)
  489. {
  490.     // Update position
  491.     sp->pos.h += (sp->vector.lh >> 16);
  492.     sp->pos.v += (sp->vector.lv >> 16);
  493.     if (sp->pos.h < gPlayRect.left)
  494.         sp->pos.h += gPlayWidth;
  495.     if (sp->pos.v < gPlayRect.top)
  496.         sp->pos.v += gPlayHeight;
  497.     if (sp->pos.h > gPlayRect.right)
  498.         sp->pos.h -= gPlayWidth;
  499.     if (sp->pos.v > gPlayRect.bottom)
  500.         sp->pos.v -= gPlayHeight;
  501.  
  502.     if (--sp->lifeSpan == 0) {
  503.         ExplodeSprite(sp,30);
  504.         NewQuake();
  505.         return;
  506.     }
  507.  
  508.     // Update Animation Frame
  509.     sp->tickCtr++;
  510.     if (sp->tickCtr >= sp->aniSpeed) {
  511.         sp->tickCtr = 0;
  512.         if (++sp->aniState >= sDef[sp->type].nbrIcons)
  513.             sp->aniState = 0;
  514.         if ((sp->aniState & 1)) {
  515.             LongPoint    bulletVec;
  516.             if (gShip && gShip->type == ST_Teapot || gShip->type == ST_TeapotT) {
  517.                 double scale;
  518.  
  519.                 bulletVec.lh = gShip->pos.h - sp->pos.h;
  520.                 bulletVec.lv = gShip->pos.v+12 - sp->pos.v;            
  521.                 scale = 1.0 / sqrt(    (double) bulletVec.lh*bulletVec.lh +
  522.                         bulletVec.lv*bulletVec.lv);
  523.                 bulletVec.lh *= scale * 65536.0;
  524.                 bulletVec.lv *= scale * 65536.0;
  525.                 bulletVec.lh <<= 2;
  526.                 bulletVec.lv <<= 2;
  527.  
  528.                 sp->vector = bulletVec;
  529.             }
  530.         }
  531.     }
  532. }
  533.  
  534. void CubeMove(register SpriteInstance *sp)
  535. {
  536.     // Update position
  537.     sp->pos.h += (sp->vector.lh >> 16);
  538.     sp->pos.v += (sp->vector.lv >> 16);
  539.     if (sp->pos.h < gPlayRect.left)
  540.         sp->pos.h += gPlayWidth;
  541.     if (sp->pos.v < gPlayRect.top)
  542.         sp->pos.v += gPlayHeight;
  543.     if (sp->pos.h > gPlayRect.right)
  544.         sp->pos.h -= gPlayWidth;
  545.     if (sp->pos.v > gPlayRect.bottom)
  546.         sp->pos.v -= gPlayHeight;
  547.  
  548.     if (--sp->lifeSpan == 0) {
  549.         ExplodeSprite(sp,30);
  550.         NewQuake();
  551.         return;
  552.     }
  553.  
  554.     // Update Animation Frame
  555.     sp->tickCtr++;
  556.     if (sp->tickCtr >= sp->aniSpeed) {
  557.         sp->tickCtr = 0;
  558.         if (++sp->aniState >= sDef[sp->type].nbrIcons)
  559.             sp->aniState = 0;
  560.         if ((sp->aniState & 1)) {
  561.             LongPoint    bulletVec;
  562.             if (gShip && gShip->type == ST_Teapot || gShip->type == ST_TeapotT) {
  563.                 double scale;
  564.  
  565.                 bulletVec.lh = gShip->pos.h - sp->pos.h;
  566.                 bulletVec.lv = gShip->pos.v+12 - sp->pos.v;            
  567.                 scale = 1.0 / sqrt(    (double) bulletVec.lh*bulletVec.lh +
  568.                         bulletVec.lv*bulletVec.lv);
  569.                 bulletVec.lh *= scale * 65536.0;
  570.                 bulletVec.lv *= scale * 65536.0;
  571.                 bulletVec.lh <<= 2;
  572.                 bulletVec.lv <<= 2;
  573.  
  574.                 sp->vector = bulletVec;
  575.             }
  576.         }
  577.         if (sp->aniState == 9) {
  578.                 LaunchBullet(ST_Photon,
  579.                              sp->pos.h+16,sp->pos.v+16,
  580.                              sp->vector.lh << 2,sp->vector.lv << 2,
  581.                              25,
  582.                              BF_BadBullet);
  583.  
  584.                 PlaySound(S_EnemyFires, 2);
  585.         }
  586.     }
  587. }
  588.  
  589.  
  590. void ShipMove(register SpriteInstance *sp)
  591. {
  592.     register short j;
  593.     register SpriteInstance *spj;
  594.     register Point bPos,tPos;
  595.     static Point firingPos[MaxAngles/2] = 
  596.        {4,16,    5,21,    7,25,    10,28,        
  597.         13,31,    18,30,    19,28,    23,23,
  598.         26,16,    24,11,    20,4,    18,3,
  599.         13,0,    10,4,    8,7,    6,12};
  600.         
  601.     // Check for Collision
  602.     bPos = sp->pos;
  603.     for (j = 0,spj=sTable; j < gMaxSprite; ++j,++spj) {
  604.         tPos = spj->pos;
  605.         if (spj->active &&                        // Active
  606.             bPos.h+32 > tPos.h &&                // In Range
  607.             bPos.v+32 > tPos.v &&
  608.             bPos.h < tPos.h+32 &&
  609.             bPos.v < tPos.v+32 &&
  610.             (BF_GoodBullet & (1L << spj->type)) > 0)        // Valid Target
  611.         {
  612.             register Ptr    mp2,mp1;
  613.             register short    x,y;
  614.             Rect            r;
  615.             r.left = max(bPos.h,tPos.h);
  616.             r.top = max(bPos.v,tPos.v);
  617.             r.right = min(bPos.h+32,tPos.h+32);
  618.             r.bottom = min(bPos.v+32,tPos.v+32);
  619.             mp1 = sDef[sp->type].maskMaps + AniFrameIndex(sp->aniState);
  620.             mp2 = sDef[spj->type].maskMaps + AniFrameIndex(spj->aniState);
  621.             for (y = r.top; y < r.bottom; ++y) {
  622.                 for (x = r.left; x < r.right; ++x) {
  623.                     if (!mp2[(x - bPos.h)+AniRowIndex((y - bPos.v))] &&
  624.                         !mp2[(x - tPos.h)+AniRowIndex((y - tPos.v))])    
  625.                         goto Collision;
  626.                 }
  627.             }
  628.         }
  629.     }
  630.  
  631.     if (sp->param1 & SF_Left) {
  632.         sp->angle -= 2;
  633.         if (sp->angle < 0)
  634.             sp->angle = MaxAngles-2;
  635.     }
  636.     else if (sp->param1 & SF_Right) {
  637.         sp->angle += 2;
  638.         if (sp->angle >= MaxAngles)
  639.             sp->angle = 0;
  640.     }
  641.  
  642.     // Update Animation Frame
  643.     sp->aniState = sp->angle >> 1;
  644.  
  645.  
  646.     if (sp->param1 & SF_Thrust) {
  647.         sp->vector.lh += vecTable[sp->angle].lh;
  648.         sp->vector.lv += vecTable[sp->angle].lv;
  649.         if (sp->type != ST_TeapotT) {
  650.             sp->type = ST_TeapotT;
  651.             PlaySound(S_Thrust, 1);
  652.         }
  653.     }
  654.     else if (sp->param1 & SF_Shield) {
  655.         if (sp->type != ST_TeapotS) {
  656.             sp->type = ST_TeapotS;
  657.             PlaySound(S_Shield, 1);
  658.         }
  659.     }
  660.     else
  661.         sp->type = ST_Teapot;
  662.     
  663.  
  664.     if (sp->param1 & SF_Fire) {
  665.         LaunchBullet(    ST_Bullet,
  666.                         bPos.h+firingPos[sp->aniState].h,
  667.                         bPos.v+firingPos[sp->aniState].v,
  668.                         sp->vector.lh + (vecTable[sp->angle].lh << 3),
  669.                         sp->vector.lv + (vecTable[sp->angle].lv << 3),
  670.                         25,
  671.                         BF_GoodBullet);
  672.         if (gShipMode & SM_Triple) {
  673.             LaunchBullet(    ST_Bullet,
  674.                             bPos.h+firingPos[sp->aniState].h,
  675.                             bPos.v+firingPos[sp->aniState].v,
  676.                             sp->vector.lh + (vecTable[(sp->angle+MaxAngles-1)%MaxAngles].lh << 3),
  677.                             sp->vector.lv + (vecTable[(sp->angle+MaxAngles-1)%MaxAngles].lv << 3),
  678.                             25,
  679.                             BF_GoodBullet);
  680.             LaunchBullet(    ST_Bullet,
  681.                             bPos.h+firingPos[sp->aniState].h,
  682.                             bPos.v+firingPos[sp->aniState].v,
  683.                             sp->vector.lh + (vecTable[(sp->angle+MaxAngles+1)%MaxAngles].lh << 3),
  684.                             sp->vector.lv + (vecTable[(sp->angle+MaxAngles+1)%MaxAngles].lv << 3),
  685.                             25,
  686.                             BF_GoodBullet);
  687.         }
  688.         PlaySound(S_Firing, 1);
  689.     }
  690.     
  691.     // Update position
  692.     sp->pos.h += (sp->vector.lh >> 16);
  693.     sp->pos.v += (sp->vector.lv >> 16);
  694.     if (sp->pos.h < gPlayRect.left)
  695.         sp->pos.h += gPlayWidth;
  696.     if (sp->pos.v < gPlayRect.top)
  697.         sp->pos.v += gPlayHeight;
  698.     if (sp->pos.h > gPlayRect.right)
  699.         sp->pos.h -= gPlayWidth;
  700.     if (sp->pos.v > gPlayRect.bottom)
  701.         sp->pos.v -= gPlayHeight;
  702.  
  703.     sp->tickCtr++;
  704.     sp->param1 &= ~SF_Fire;
  705.     return;
  706. Collision:
  707.     if (!(sp->param1 & SF_Shield) &&
  708.         spj->type != ST_Yummies) {
  709.         ExplodeSprite(sp,30);
  710.         NewShip();
  711.     }
  712.  
  713.     switch (spj->type) {
  714.     case ST_Wheel:
  715.         NewSubAsteroid(j,ST_WheelR,-2);
  716.         NewSubAsteroid(j,ST_WheelG,0);
  717.         NewSubAsteroid(j,ST_WheelB,2);
  718.         --gAsteroidCnt;
  719.         KillSprite(spj);
  720.         PlaySound(S_CompoundFracture, 2);
  721.         break;
  722.     case ST_Aster:
  723.         NewSubAsteroid(j,ST_Aster1,-2);
  724.         NewSubAsteroid(j,ST_Aster2,2);
  725.         --gAsteroidCnt;
  726.         KillSprite(spj);
  727.         PlaySound(S_CompoundFracture, 2);
  728.         break;
  729.     case ST_WheelR:
  730.     case ST_WheelG:
  731.     case ST_WheelB:
  732.     case ST_Aster1:
  733.     case ST_Aster2:
  734.     case ST_Teacup:
  735.     case ST_Jim:
  736.         --gAsteroidCnt;
  737.         ExplodeSprite(spj,10);
  738.         break;
  739.     case ST_Yummies:
  740.         ConsumeYummy(spj);
  741.         break;
  742.     case ST_Saucer:
  743.         ExplodeSprite(spj,10);
  744.         break;
  745.     case ST_Barbell:
  746.         ExplodeSprite(spj,10);
  747.         break;
  748.     case ST_Cube:
  749.         ExplodeSprite(spj,10);
  750.         break;
  751.     }
  752. }
  753.  
  754. void BulletMove(register SpriteInstance *sp)
  755. {
  756.     register short j,n;
  757.     register SpriteInstance *spj;
  758.     register Point    bPos,tPos;
  759.  
  760.     // Update position
  761.     sp->pos.h += (sp->vector.lh >> 16);
  762.     sp->pos.v += (sp->vector.lv >> 16);
  763.     if (sp->pos.h < gPlayRect.left)
  764.         sp->pos.h += gPlayWidth;
  765.     if (sp->pos.v < gPlayRect.top)
  766.         sp->pos.v += gPlayHeight;
  767.     if (sp->pos.h > gPlayRect.right)
  768.         sp->pos.h -= gPlayWidth;
  769.     if (sp->pos.v > gPlayRect.bottom)
  770.         sp->pos.v -= gPlayHeight;
  771.  
  772.     // Update Animation Frame
  773.     if (--sp->lifeSpan == 0) {
  774.         KillSprite(sp);
  775.         return;
  776.     }
  777.     // Check for Collision
  778.     bPos = sp->pos;
  779.     n = gMaxSprite;
  780.     for (j = 0,spj=sTable; j < n; ++j,++spj) {
  781.         tPos = spj->pos;
  782.         if (spj->active &&                            // Active
  783.             // abs((bPos.h >> 5) - (tPos.h >> 5)) < 2 &&    // Range Check
  784.             // abs((bPos.v >> 5) - (tPos.v >> 5)) < 2 &&    // Range Check
  785.             bPos.h+2 > tPos.h &&                // In Range
  786.             bPos.v+2 > tPos.v &&
  787.             bPos.h < tPos.h+32 &&
  788.             bPos.v < tPos.v+32 &&
  789.             (sp->param1 & (1L << spj->type)) > 0)        // Valid Target
  790.         {
  791.             register Ptr    mp;
  792.             Rect            r;
  793.             register short y,x;
  794.             r.left = max(sp->pos.h,tPos.h);
  795.             r.top = max(sp->pos.v,tPos.v);
  796.             r.right = min(sp->pos.h+2,tPos.h+32);
  797.             r.bottom = min(sp->pos.v+2,tPos.v+32);
  798.             mp = sDef[spj->type].maskMaps + AniFrameIndex(spj->aniState);
  799.             for (y = r.top; y < r.bottom; ++y) {
  800.                 for (x = r.left; x < r.right; ++x) {
  801.                     if (!mp[(x - tPos.h)+AniRowIndex((y - tPos.v))])    
  802.                         goto Collision;
  803.                 }
  804.             }
  805.         }
  806.     }
  807.     return;
  808.  
  809. Collision:
  810.     KillSprite(sp);        // Kill Bullet
  811.  
  812.     switch (spj->type) {
  813.     case ST_Wheel:
  814.         NewSubAsteroid(j,ST_WheelR,-2);
  815.         NewSubAsteroid(j,ST_WheelG,0);
  816.         NewSubAsteroid(j,ST_WheelB,2);
  817.         --gAsteroidCnt;
  818.         KillSprite(spj);
  819.         AddScore(AsterPoints);
  820.         PlaySound(S_CompoundFracture, 2);
  821.         break;
  822.     case ST_Aster:
  823.         NewSubAsteroid(j,ST_Aster1,-2);
  824.         NewSubAsteroid(j,ST_Aster2,2);
  825.         --gAsteroidCnt;
  826.         KillSprite(spj);
  827.         AddScore(AsterPoints);
  828.         PlaySound(S_CompoundFracture, 2);
  829.         break;
  830.     case ST_WheelR:
  831.     case ST_WheelG:
  832.     case ST_WheelB:
  833.     case ST_Aster1:
  834.     case ST_Aster2:
  835.     case ST_Teacup:
  836.     case ST_Jim:
  837.         --gAsteroidCnt;
  838.         ExplodeSprite(spj,10);
  839.         AddScore(AsterPoints);
  840.         break;
  841.     case ST_Saucer:
  842.         if (++spj->param2 >= spj->param1)    {        // Max Hits?
  843.             ExplodeSprite(spj,20);
  844.             AddScore(SaucerPoints);
  845.         }
  846.         break;
  847.     case ST_Barbell:
  848.         if (++spj->param2 >= spj->param1)    {        // Max Hits?
  849.             ExplodeSprite(spj,20);
  850.             AddScore(BarbellPoints);
  851.         }
  852.         break;
  853.     case ST_Cube:
  854.         if (++spj->param2 >= spj->param1)    {        // Max Hits?
  855.             ExplodeSprite(spj,20);
  856.             AddScore(CubePoints);
  857.         }
  858.         break;
  859.     case ST_Teapot:
  860.     case ST_TeapotT:
  861.         ExplodeSprite(spj,30);
  862.         NewShip();
  863.         break;
  864.     case ST_Yummies:
  865.         StandardSpriteErase(spj);
  866.         ExplodeSprite(spj,10);    // No Points for Exploded Yummies
  867.         break;
  868.     case ST_TeapotS:    // Nada - Shields are up
  869.         PlaySound(S_DudBullet, 2);
  870.         break;
  871.     }
  872. }
  873.  
  874. void YummyMove(register SpriteInstance *sp)
  875. {
  876.     if (--sp->lifeSpan == 0) {
  877.         StandardSpriteErase(sp);
  878.         KillSprite(sp);
  879.         --gYummyCnt;
  880.         return;
  881.     }
  882. }
  883.  
  884.  
  885. void SparkMove(register SpriteInstance *sp)
  886. {
  887.     register short j;
  888.     register SpriteInstance *spj;
  889.  
  890.     // Update position
  891.     sp->pos.h += (sp->vector.lh >> 16);
  892.     sp->pos.v += (sp->vector.lv >> 16);
  893.     if (sp->pos.h < gPlayRect.left)
  894.         sp->pos.h += gPlayWidth;
  895.     if (sp->pos.v < gPlayRect.top)
  896.         sp->pos.v += gPlayHeight;
  897.     if (sp->pos.h > gPlayRect.right)
  898.         sp->pos.h -= gPlayWidth;
  899.     if (sp->pos.v > gPlayRect.bottom)
  900.         sp->pos.v -= gPlayHeight;
  901.  
  902.     // Update Animation Frame
  903.     if (--sp->lifeSpan == 0) {
  904.         --gSparkCnt;
  905.         KillSprite(sp);
  906.         return;
  907.     }
  908. }
  909.  
  910. // This gets swapped into the status sprite's move function, which is
  911. // normally null
  912. // it causes a temporary "spacequake" jitter effect, possibly causing
  913. // collisions with nearby objects
  914. //
  915. void QuakeMove(register SpriteInstance *sp)
  916. {
  917.     register short j,n,i;
  918.     register SpriteInstance *spj;
  919.     static char xOffset[] = {-2,3, 0,-4, 0,-1,2,2};
  920.     static char yOffset[] = {-4,0,-1, 2,-2, 3,2,0};
  921.     if (--sp->param2 == 0) {
  922.         sDef[ST_StatusDisplay].moveFunc = NullFunc;
  923.         return;
  924.     }
  925.     n = gMaxSprite;
  926.     i = sp->param2 & 7;
  927.     for (j = 0,spj=sTable; j < n; ++j,++spj) {
  928.         spj->pos.h += xOffset[i];
  929.         spj->pos.v += yOffset[i];
  930.         ++i;
  931.         i &= 7;
  932.     }
  933. }
  934.  
  935. void NullFunc(register SpriteInstance *sp)
  936. {
  937. }
  938.  
  939.  
  940.